﻿using PrairieAsunder.Common;
using PrairieAsunder.Models;
using Raven.Client;
using Raven.Imports.Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;

namespace PrairieAsunder.Controllers
{
    public class AdminController : Controller
    {
        public ActionResult Index()
        {
            // If there are no users, go to the login page.
            if (!this.HasAdminUser() || !User.Identity.IsAuthenticated)
            {
                return RedirectToAction("Login", "Admin");
            }

            ViewBag.UserName = User.Identity.Name;
            return View();
        }

        public ActionResult CreateUser()
        {
            return View();
        }

        public ActionResult Login()
        {
            if (!this.HasAdminUser())
            {
                ViewBag.NeedsToCreateUser = true;
                return View();
            }
            else if (!User.Identity.IsAuthenticated)
            {
                ViewBag.NeedsToCreateUser = false;
                return View();
            }
            else
            {
                return RedirectToAction("Index", "Admin");
            }
        }

        [HttpPost]
        public ActionResult Logout()
        {
            FormsAuthentication.SignOut();
            return RedirectToAction("Login", "Admin");
        }

        [HttpPost]  
        public ActionResult PersonaLogin(string assertion)  
        {
            if (assertion == null)
            {
                // The 'assertion' key of the API wasn't POSTED. Redirect.
                return RedirectToAction("Index", "Admin");
            }

            using (var web = new WebClient())
            {
                // Build the data we're going to POST.
                var data = new NameValueCollection();
                data["assertion"] = assertion;
#if DEBUG
                data["audience"] = "http://localhost:53208/"; 
#else
                data["audience"] = "http://prairieasunder.com";
#endif

                // POST the data to the Persona provider (in this case Mozilla)
                var response = web.UploadValues("https://verifier.login.persona.org/verify", "POST", data);
                var buffer = Encoding.Convert(Encoding.GetEncoding("iso-8859-1"), Encoding.UTF8, response);

                // Convert the response to JSON.
                var tempstring = Encoding.UTF8.GetString(buffer, 0, response.Length);
                dynamic result = JsonConvert.DeserializeObject(tempstring);
                if (result.status.Value == "okay")
                {
                    string email = result.email; 
                    FormsAuthentication.SetAuthCookie(email, true);
                    var isFirstUser = FirstUserOrFalse(email);
                    if (isFirstUser)
                    {
                        return RedirectToAction("Index", "Admin");
                    }
                    else
                    {
                        throw new Exception("You are not the admin user.");
                    }
                }

                throw new Exception("Invalid login.");
            }
        }

        [HttpPost]
        public void UploadSong(string address, string fileName)
        {
            if (!User.Identity.IsAuthenticated)
            {
                return;
            }

            var downloader = new System.Net.WebClient();
            var unescapedFileName = Uri.UnescapeDataString(fileName);
            var filePath = System.IO.Path.Combine(Constants.MusicDirectory, unescapedFileName);

            using (var webClient = new System.Net.WebClient())
            {
                webClient.DownloadFile(address, filePath);
            }

            using (var session = Get.A<IDocumentStore>().OpenSession())
            {
                // Find the song with the file name. If it exists, append the current milliseconds
                var existingSong = session.Query<Song>().FirstOrDefault(s => s.FileName == unescapedFileName);
                var uniqueFileName = existingSong == null ? unescapedFileName : unescapedFileName.Replace(".mp3", DateTime.Now.Millisecond.ToString() + ".mp3");
                var newSong = new Song(uniqueFileName);
                session.Store(newSong);
                session.SaveChanges();
            }
        }

        [HttpPost]
        public void SaveSong(Song song)
        {
            if (!User.Identity.IsAuthenticated)
            {
                return;
            }

            using (var session = Get.A<IDocumentStore>().OpenSession())
            {
                var dbSong = session.Load<Song>(song.Id);
                if (dbSong != null)
                {
                    // Rename the physical file if necessary.
                    if (dbSong.FileName != song.FileName)
                    {
                        var sourceFileName = System.IO.Path.Combine(Constants.MusicDirectory, dbSong.FileName);
                        var targetFileName = System.IO.Path.Combine(Constants.MusicDirectory, song.FileName);
                        System.IO.File.Move(sourceFileName, targetFileName);
                    }

                    dbSong.Artist = song.Artist;
                    dbSong.Album = song.Album;
                    dbSong.CommunityRank = song.CommunityRank;
                    dbSong.FileName = song.FileName;
                    dbSong.Name = song.Name;
                    dbSong.Number = song.Number;
                    dbSong.PurchaseUri = song.PurchaseUri;
                    session.Store(dbSong);
                    session.SaveChanges();
                }
            }
        }

        [HttpPost]
        public void DeleteSong(string id)
        {
            if (!User.Identity.IsAuthenticated)
            {
                return;
            }

            using (var session = Get.A<IDocumentStore>().OpenSession())
            {
                var song = session.Load<Song>(id);
                if (song != null)
                {
                    var filePath = System.IO.Path.Combine(Constants.MusicDirectory, song.FileName);
                    System.IO.File.Delete(filePath);

                    session.Delete(song);
                    session.SaveChanges();
                }
            }
        }

        public JsonResult GetSongs(string search, int skip, int take)
        {
            if (!User.Identity.IsAuthenticated)
            {
                return Json(Enumerable.Empty<Song>(), JsonRequestBehavior.AllowGet);
            }

            using (var session = Get.A<IDocumentStore>().OpenSession())
            {
                var query = session
                    .Query<Song>()
                    .Customize(x => x.WaitForNonStaleResultsAsOfNow(TimeSpan.FromSeconds(5)));

                if (string.IsNullOrWhiteSpace(search))
                {
                    var results = query
                        .Skip(skip)
                        .Take(take)
                        .OrderByDescending(o => o.Id);

                    return Json(results, JsonRequestBehavior.AllowGet);                    
                }
                else
                {
                    var results = query
                        .Where(s => s.Album.StartsWith(search) || s.Artist.StartsWith(search) || s.Name.StartsWith(search) || s.FileName.StartsWith(search))
                        .Skip(skip)
                        .Take(take);

                    return Json(results, JsonRequestBehavior.AllowGet);
                }
            }
        }

        private bool FirstUserOrFalse(string email)
        {
            using (var session = Get.A<IDocumentStore>().OpenSession())
            {
                if (!session.Query<AdminUser>().Any())
                {
                    var user = new AdminUser { EmailAddress = email };
                    session.Store(user);
                    session.SaveChanges();
                    return true;
                }

                var userWithEmailExists = session.Query<AdminUser>().Any(u => u.EmailAddress == email);
                return userWithEmailExists;
            }
        }

        private bool HasAdminUser()
        {
            using (var session = Get.A<IDocumentStore>().OpenSession())
            {
                return session.Query<AdminUser>().Any();
            }
        }
    }
}
